﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public static class SingletonManager {

    private static List<CraftMaterial> materials = new List<CraftMaterial>();
    private static List<EnemyClass> enemies = new List<EnemyClass>();
    private static List<Outfit> outfits = new List<Outfit>();
    private static List<Weapon> weapons = new List<Weapon>();
    private static List<Recipe> recipes = new List<Recipe>();

    static SingletonManager()
    {
        materials.AddRange(LoadType<CraftMaterialCollection>("CraftMaterials/materialcollection").materialCollection);
        enemies.AddRange(LoadType<EnemyCollection>("Enemies/enemycollection").enemyCollection);
        outfits.AddRange(LoadType<OutfitCollection>("Outfits/outfitcollection").outfitCollection);
        weapons.AddRange(LoadType<WeaponCollection>("Weapons/weaponcollection").weaponCollection);
        recipes.AddRange(LoadType<RecipeCollection>("Recipes/recipecollection").recipeCollection);
    }

    private static T LoadType<T>(string filepath)
    {
        TextAsset loadedAsset = Resources.Load<TextAsset>(filepath);
        return JsonUtility.FromJson<T>(loadedAsset.text);
    }

    public static IRecipeOutput Find(string key)
    {
        if (key.StartsWith("weapon")) return FindWeapon(key);
        else if (key.StartsWith("outfit")) return FindOutfit(key);
        else if (key.StartsWith("material")) return FindMaterial(key);
        else return null;
    }

    #region Getters
    public static List<CraftMaterial> getMaterials()
    {
        return materials;
    }

    public static List<EnemyClass> getEnemies()
    {
        return enemies;
    }

    public static List<Outfit> getOutfits()
    {
        return outfits;
    }

    public static List<Weapon> getWeapons()
    {
        return weapons;
    }

    public static List<Recipe> getRecipes()
    {
        return recipes;
    }
    #endregion

    #region Material
    public static CraftMaterial FindMaterial(string p_key)
    {
        foreach(CraftMaterial mat in materials)
        {
            if (mat.key == p_key) return mat;
        }
        return null;
    }

    public static Sprite GetSprite(CraftMaterial p_mat)
    {
        return Resources.Load<Sprite>("CraftMaterials/img/" + p_mat.key);
    }

    public static List<CraftMaterialAmount> GenerateLootList(int p_dropCount)
    {
        if (p_dropCount <= 0) throw new System.Exception("LootGenerateException: Size of the loot table can not be equal to or smaller than 0");
        List<CraftMaterialAmount> result = new List<CraftMaterialAmount>();

        List<MaterialIntRange> dropTable = new List<MaterialIntRange>();
        for (int i = 0; i < materials.Count; i++)
        {
            if (dropTable.Count == 0)
            {
                dropTable.Add(new MaterialIntRange(materials[i].key, new IntRange(0, materials[i].rarity)));
            }
            else
            {
                dropTable.Add(new MaterialIntRange(materials[i].key, new IntRange(dropTable[i-1].range.GetMax() + 1, materials[i].rarity + dropTable[i-1].range.GetMax() + 1)));
            }
        }

        for (int i = 0; i < p_dropCount; i++)
        {
            int pickedNum = Random.Range(0, dropTable[dropTable.Count - 1].range.GetMax());
            foreach (MaterialIntRange rng in dropTable)
            {
                if (rng.range.Contains(pickedNum))
                {
                    CraftMaterial picked = FindMaterial(rng.materialKey);
                    bool found = false;
                    foreach (CraftMaterialAmount cma in result)
                    {
                        if (cma.material.key == picked.key)
                        {
                            cma.amount++;
                            found = true;
                        }
                    }
                    if (!found) result.Add(new CraftMaterialAmount(picked));
                }
            }
        }
        return result;
    }
    #endregion

    #region Enemy
    public static EnemyClass FindEnemy(string p_key)
    {
        foreach(EnemyClass enm in enemies)
        {
            if (enm.key == p_key) return enm;
        }
        return null;
    }

    public static Sprite GetSprite(EnemyClass p_enm)
    {
        return Resources.Load<Sprite>("Enemies/img/" + p_enm.key);
    }

    public static EnemyClass GetRandomEnemy()
    {
        return enemies[Random.Range(0, enemies.Count)];
    }
    #endregion

    #region Outfit
    public static Outfit FindOutfit(string p_key)
    {
        foreach(Outfit o in outfits)
        {
            if (o.key == p_key) return o;
        }
        return null;
    }

    public static Sprite GetSprite(Outfit p_o)
    {
        return Resources.Load<Sprite>("Outfits/img/" + p_o.key);
    }
    #endregion

    #region Weapon
    public static Weapon FindWeapon(string p_key)
    {
        foreach(Weapon w in weapons)
        {
            if (w.key == p_key) return w;
        }
        return null;
    }

    public static Sprite GetSprite(Weapon p_w)
    {
        return Resources.Load<Sprite>("Weapons/img/" + p_w.key);
    }
    #endregion

    #region Recipe
    public static Recipe FindRecipe(string p_key)
    {
        foreach (Recipe r in recipes)
        {
            if (r.key == p_key) return r;
        }
        return null;
    }

    public static Sprite GetOutputSprite(Recipe r)
    {
        if (r.output.StartsWith("weapon"))
        {
            return GetSprite(FindWeapon(r.output));
        }
        else if (r.output.StartsWith("outfit"))
        {
            return GetSprite(FindOutfit(r.output));
        }
        else if (r.output.StartsWith("material"))
        {
            return GetSprite(FindMaterial(r.output));
        }
        else
        {
            return null;
        }
    }
    #endregion
}

internal class IntRange
{
    private int min, max;

    public IntRange(int p_min, int p_max)
    {
        min = p_min;
        max = p_max;
    }

    public int GetMin()
    {
        return min;
    }

    public int GetMax()
    {
        return max;
    }

    public bool Contains(int num)
    {
        return num >= min && num <= max;
    }

    public int[] ToArray()
    {
        List<int> returnable = new List<int>();
        for (int i = min; i <= max; i++)
        {
            returnable.Add(i);
        }
        return returnable.ToArray();
    }
}

internal class MaterialIntRange
{
    public string materialKey;
    public IntRange range;

    public MaterialIntRange(string p_matKey, IntRange p_range)
    {
        materialKey = p_matKey;
        range = p_range;
    }
}